Encrypt attribute values manually without needing save callback#150
Encrypt attribute values manually without needing save callback#150
Conversation
|
I'm a little uneasy at changing where in the lifecycle the write happens - at least without an explicit opt-in for the change in behavior. Ideally, the writes can happen after save even in an import scenario. I'd suggest looking into one of these paths:
Sure, it may be a little slower than your current solution, but I'd trade a little bit of speed for more correctness (allowing for bulk inserts, but callbacks are still called after the database transaction).
Flavor A: Add a flag to opt-in to this new behavior: class Person < ActiveRecord::Base
include Vault::EncryptedModel
vault_attribute :ssn, encrypt_on_set: true # opt-in for activerecord-import
endFlavor B: Add an explicit encrypt method to this API # Then, at the caller, you can explicitly write the encrypted values ahead of the import cycle:
people = 1000.times.map { |i|
Person.new(ssn: "123-45-#{i}").tap(&:vault_encrypt_attributes!)
}
Person.import(people)
# This is essentially just publicizing your new `__vault_write_encrypted_attribute!` interfaceAny of these paths might yield leaving the default behavior as-is while still allowing for bulk import scenarios. |
6599b9d to
0ef2caf
Compare
0ef2caf to
934d21e
Compare
Splits __vault_persist_attribute!, which is normally called on each attribute after_save, into two methods. The new method, __vault_write_encrypted_attribute!, is also called on each vault attribute when `vault_encrypt_attributes!` is called. > p = Person.new(ssn: "123-45-6789").vault_encrypt_attributes! > p.ssn_encrypted "vault:dev:flu/yp9oeYYFgjcZH2hVBA=="
dd47dae to
c06c8ae
Compare
|
@chrisarcand Thanks for the ideas to leave the existing behavior intact. I was hesitant to expand the public interface at first but I think it is the right move to add an explicit encrypt method the the model. I felt the config options like encrypt_on_set weren't suitable because the import use case shouldn't be dependent on the model definition. |
chrisarcand
left a comment
There was a problem hiding this comment.
Looks reasonable to me! Quick question...
| RAILS_VERSION = ENV.fetch("RAILS_VERSION", "6.0.0") | ||
|
|
||
| gem "rails", "~> #{RAILS_VERSION}" | ||
| gem "concurrent-ruby", "1.3.4" |
There was a problem hiding this comment.
All rails versions before 7.1 have a direct dependency on concurrent-ruby (We use an older version for testing), but concurrent-ruby versions 1.3.5+ removed the dependency on Logger. When using Rails 7.0- and concurrent-ruby 1.3.5+, you get uninitialized constant ActiveSupport::LoggerThreadSafeLevel::Logger (NameError) when starting tests-- so I just pinned the version to 1.3.4 instead of upgrading the dummy rails installation.
There was a problem hiding this comment.
Added a clarifying comment to the Gemfile. This is not a gem dependency, just a developer one
README.md
Outdated
| > nil | ||
| p.vault_encrypt_attributes! | ||
| p.ssn_encrypted | ||
| > "vault:dev:flu/yp9oeYYFgjcZH2hVBA==" |
There was a problem hiding this comment.
Maybe add a p.persisted? to demo that it isn't saved.
Description
I'd like vault-rails to set the *_encrypted value attribute without calling save. This way, it can be used with gems like activerecord-import that create many records at once.
Implementation Notes
Splits
__vault_persist_attribute!, which is normally called on each attribute after_save, into two methods. The new method,__vault_write_encrypted_attribute!, is also called on each attribute whenvault_encrypt_attributes!is called on the model